package com.baymax.test.scene.demo;

import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_CURTAIN_X;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_CURTAIN_Y;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_CIRCLE;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_NONE;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_RANDOM;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_RECT;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_SHUTTER_HORIZONTAL;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_SHUTTER_VERTICAL;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_TRANSLATION_DOWN_UP;
import static com.baymax.test.scene.demo.PhotoView.AnimationMode.ANIMATION_TRANSLATION_UP_DOWN;
import static java.lang.annotation.RetentionPolicy.SOURCE;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.util.Log;
import android.util.Property;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;

import com.facebook.common.internal.ImmutableList;
import com.facebook.drawee.view.SimpleDraweeView;

import java.lang.annotation.Retention;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PhotoView extends FrameLayout implements Animator.AnimatorListener {


    private static final String TAG = "Photo_View";
    private final Switcher mSwitcher = new Switcher(Collections.emptyList());

    /**
     * 上层图片
     * 动画目前都是针对master的
     */
    private final SimpleDraweeView v1 = new SimpleDraweeView(getContext());
    /**
     * 下层图片
     */
    private final SimpleDraweeView v2 = new SimpleDraweeView(getContext());

    private final SimpleDraweeView mMaster = v1;
    private final SimpleDraweeView mSlave = v2;

    private ObjectAnimator animator;

    private boolean destroyed;
    private int mode = ANIMATION_NONE;

    @Override
    public void onAnimationStart(@NonNull Animator animation) {
        Log.d(TAG, "onAnimationStart: " + mSwitcher);

    }

    @Override
    public void onAnimationEnd(@NonNull Animator animation) {
        mSwitcher.increase();
        mDrawer.markAppendCommit();
        notifyChangeSource();
        startSwitcherAnimator(mDrawer.getCurrentDrawer());
        Log.d(TAG, "onAnimationEnd: " + mSwitcher);
    }

    private void notifyChangeSource() {
        int currentIndex = mSwitcher.currentIndex;
        //确定主次
//        if (currentIndex % 2 == 0) {
//            mMaster = v1;
//            mSlave = v2;
//        } else {
//            mMaster = v2;
//            mSlave = v1;
//        }
        //只需要设置
        Log.d(TAG, "notifyChangeSource: t1" + mMaster.getTag());
        Log.d(TAG, "notifyChangeSource: t2" + mSlave.getTag());
        mMaster.setImageURI(mSwitcher.current());
        mMaster.setTag(mSwitcher.current());
        mSlave.setImageURI(mSwitcher.next());
        mSlave.setTag(mSwitcher.next());
        invalidate();
    }

    @Override
    public void onAnimationCancel(@NonNull Animator animation) {
        Log.d(TAG, "onAnimationCancel: ");
    }

    @Override
    public void onAnimationRepeat(@NonNull Animator animation) {

    }

    public void pause() {
        animator.pause();
    }


    @Retention(SOURCE)
    @IntDef({ANIMATION_NONE, ANIMATION_CURTAIN_X, ANIMATION_CURTAIN_Y,
            ANIMATION_TRANSLATION_UP_DOWN, ANIMATION_TRANSLATION_DOWN_UP,
            ANIMATION_RECT, ANIMATION_CIRCLE,
            ANIMATION_SHUTTER_HORIZONTAL, ANIMATION_SHUTTER_VERTICAL,
            ANIMATION_RANDOM,

    })
    public @interface AnimationMode {
        /**
         * 无动画
         */
        int ANIMATION_NONE = 0;
        /**
         * Y轴向下
         */
        int ANIMATION_CURTAIN_X = 1;
        int ANIMATION_CURTAIN_Y = 2;
        int ANIMATION_TRANSLATION_DOWN_UP = 3;
        int ANIMATION_TRANSLATION_UP_DOWN = 4;
        int ANIMATION_RECT = 5;
        int ANIMATION_CIRCLE = 6;
        int ANIMATION_SHUTTER_HORIZONTAL = 7;
        int ANIMATION_SHUTTER_VERTICAL = 8;
        int ANIMATION_RANDOM = 999;


    }

    private final SwitchDrawer none = new NoneSwitcher(this, v1, v2);
    private final LazyUpdateDrawer mDrawer = new LazyUpdateDrawer(none);

    public PhotoView(@NonNull Context context) {
        super(context);
        init();
    }

    private void init() {
        addView(mSlave, LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        addView(mMaster, LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        animator = ObjectAnimator.ofPropertyValuesHolder(this, PropertyValuesHolder.ofFloat(PERCENT_PROP, 0f, 1f));
        animator.setInterpolator(new LinearOutSlowInInterpolator());
        animator.setDuration(3000);
        animator.setStartDelay(2000);
    }

    private static final Property<PhotoView, Float> PERCENT_PROP = new Property<PhotoView, Float>(Float.class, "percent") {
        @Override
        public Float get(PhotoView object) {
            return object.mDrawer.getCurrentDrawer().percent;
        }

        @Override
        public void set(PhotoView object, Float value) {
            object.mDrawer.currentDrawer.percent = value;
            object.invalidate();
        }
    };

    public void setDataUrls(@NonNull List<String> urls) {
        Log.d(TAG, "setDataUrls: " + urls);
        mDrawer.cancel();
        int current = Math.min(mSwitcher.currentIndex, urls.size());
        mSwitcher.updateSource(urls, current);
        notifyChangeSource();
    }


    public void setDrawer(@AnimationMode int mode) {
        if (this.mode == mode) {
            Log.d(TAG, "setDrawer: eq");
            return;
        }
        this.mode = mode;
        SwitchDrawer drawer = createDrawer(mode);
        SwitchDrawer oldDrawer = this.mDrawer.getCurrentDrawer();
        boolean running = animator.isRunning();
        Log.d(TAG, "setDrawer:r " + running);
        this.mDrawer.appendNextDrawer(drawer);
        if (!running) {
            mDrawer.markAppendCommit();
        }
        boolean started = animator.isStarted();
        Log.d(TAG, "setDrawer:s " + started);
        this.mDrawer.appendNextDrawer(drawer);
        if (!(started || running)) {
            startSwitcherAnimator(mDrawer.getCurrentDrawer());
        }
        if (animator.isPaused()) {
            animator.resume();
        }

    }


    @NonNull
    private SwitchDrawer createDrawer(int mode) {
        SwitchDrawer drawer;
        if (mode == ANIMATION_CURTAIN_X || mode == ANIMATION_CURTAIN_Y) {
            CurtainSwitcher drawerCurtain = new CurtainSwitcher(this, mMaster, mSlave);
            drawerCurtain.setOriention(mode == ANIMATION_CURTAIN_X ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL);
            drawer = drawerCurtain;
        } else if (mode == ANIMATION_RANDOM) {
            ComposeRandomSwitcher randomDrawer = new ComposeRandomSwitcher(this, mMaster, mSlave);
            randomDrawer.setChildDrawers(ImmutableList.of(
                    createDrawer(ANIMATION_SHUTTER_HORIZONTAL),
                    createDrawer(ANIMATION_SHUTTER_VERTICAL),
                    createDrawer(ANIMATION_RECT),
                    createDrawer(ANIMATION_CIRCLE),
                    createDrawer(ANIMATION_CURTAIN_X),
                    createDrawer(ANIMATION_CURTAIN_Y),
                    createDrawer(ANIMATION_TRANSLATION_DOWN_UP),
                    createDrawer(ANIMATION_TRANSLATION_UP_DOWN)
            ));
            drawer = randomDrawer;

        } else if (mode == ANIMATION_TRANSLATION_UP_DOWN || mode == ANIMATION_TRANSLATION_DOWN_UP) {
            TranslationSwitcher translationSwitcher = new TranslationSwitcher(this, mMaster, mSlave);
            translationSwitcher.arrow = mode - ANIMATION_TRANSLATION_DOWN_UP;
            drawer = translationSwitcher;
        } else if (mode == ANIMATION_RECT) {
            drawer = new RectSwitcher(this, mMaster, mSlave);
        } else if (mode == ANIMATION_CIRCLE) {
            drawer = new CircleSwitcher(this, mMaster, mSlave);
        } else if (mode == ANIMATION_SHUTTER_HORIZONTAL || mode == ANIMATION_SHUTTER_VERTICAL) {
            ShutterInSwitcher shutterSwitcher = new ShutterInSwitcher(this, mMaster, mSlave);
            shutterSwitcher.setOriention(mode - ANIMATION_SHUTTER_HORIZONTAL);
            drawer = shutterSwitcher;
        } else {
            drawer = new NoneSwitcher(this, mMaster, mSlave);
        }
        Log.d(TAG, "createDrawer: " + drawer);
        return drawer;
    }

    private void startSwitcherAnimator(SwitchDrawer currentDrawer) {
        if (destroyed) {
            Log.d(TAG, "startSwitcherAnimator: destroyed");
            return;
        }
        Animator animator = this.animator;
        if (animator.getInterpolator() == null) {
            animator.setInterpolator(new LinearOutSlowInInterpolator());
        }
        currentDrawer.decoderAnimator(animator);
        Log.d(TAG, "startSwitcherAnimator: " + animator);
        animator.removeListener(this);
        animator.addListener(this);
        animator.start();
    }

    final Paint textPaint = new Paint();

    {

        textPaint.setColor(Color.RED);
    }


    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (mDrawer == null || !animator.isRunning()) {
            //如果不在动画中、只要画master即可
            super.dispatchDraw(canvas);
            dispatchDebugInfo(canvas, null);
            return;
        }
        SwitchDrawer currentDrawer = mDrawer.getCurrentDrawer();
        currentDrawer.disptachDraw(canvas);
        dispatchDebugInfo(canvas, currentDrawer);

    }

    private void dispatchDebugInfo(Canvas canvas, SwitchDrawer currentDrawer) {
        canvas.drawText(mSwitcher.currentIndex + "", 16f, 16f, textPaint);
        canvas.drawText(animator.isStarted() + "," + animator.isRunning(), 16f, 32f, textPaint);
        if (currentDrawer != null) {
            canvas.drawText("per:" + (currentDrawer).percent, 16f, 48f, textPaint);
        }
    }

    public void onDestroy() {
        try {
            destroyed = true;
            mDrawer.cancel();
        } catch (Exception e) {
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        onDestroy();
    }

    private void createAnimationParams(List<String> urls) {

    }


    static class AnimationParam {
        @AnimationMode
        final int animationType;

        AnimationParam(int animationType) {
            this.animationType = animationType;
        }
    }

    static class Switcher {

        private static final String empty = "";
        private final List<String> requests = new ArrayList<>();
        private int currentIndex = 0;

        public Switcher(@NonNull List<String> urls) {
            updateSource(urls, 0);
        }

        public void updateSource(@NonNull List<String> urls, int current) {
            if (current < 0 || current > urls.size()) {
                throw new IllegalArgumentException("current index gt than urls size !");
            }
            this.currentIndex = current;
            requests.clear();
            requests.addAll(urls);
        }

        @NonNull
        public String next() {
            return getAtIndex(currentIndex + 1);
        }

        private String getAtIndex(int currentIndex) {
            if (requests.isEmpty()) {
                return empty;
            }
            if (currentIndex >= size()) {
                //first
                return requests.get(0);
            }
            return requests.get(currentIndex);
        }


        public void increase() {
            int c = currentIndex;
            int next = c + 1;
            if (next >= requests.size()) {
                next = 0;
            }
            this.currentIndex = next;
        }

        public int size() {
            return requests.size();
        }


        public String current() {
            return getAtIndex(currentIndex);
        }

        @Override
        public String toString() {
            return "Switcher{" +
                    "currentIndex=" + currentIndex +
                    " , requests=" + requests +
                    '}';
        }
    }


    static class NoneSwitcher extends SwitchDrawer {


        public NoneSwitcher(PhotoView continer, View master, View slave) {
            super(continer, master, slave);
        }

        @Override
        public void disptachDraw(Canvas canvas) {

        }

        @Override
        public void cancel() {

        }

    }

    static abstract class SwitchDrawer {

        protected float percent = 0f;


        protected PhotoView continer;
        protected View master;
        protected View slave;

        public SwitchDrawer(PhotoView continer, View master, View slave) {
            this.continer = continer;
            this.master = master;
            this.slave = slave;
        }

        public abstract void disptachDraw(Canvas canvas);

        public void cancel() {

        }

        public void decoderAnimator(Animator animator) {

        }
    }

    static class CurtainSwitcher extends SwitchDrawer {

        private final Rect mRect = new Rect();

        private int oriention = LinearLayout.HORIZONTAL;

        public void setOriention(int oriention) {
            this.oriention = oriention;

        }

        public CurtainSwitcher(PhotoView container, View mater, View slave) {
            super(container, mater, slave);
        }


        @Override
        public void disptachDraw(Canvas canvas) {
            int save = canvas.save();
            slave.draw(canvas);
            int width = continer.getWidth();
            int height = continer.getHeight();
            Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            Canvas maskCanvas = new Canvas(mask);
            maskCanvas.translate(master.getScrollX(), master.getScrollY());
            //生成bitmap
            master.draw(maskCanvas);
            //画需要的显示的部分
            if (oriention == LinearLayout.HORIZONTAL) {
                mRect.set(Math.round(width * percent), 0, width, height);
            } else {
                mRect.set(0, Math.round(height * percent), width, height);
            }
            canvas.drawBitmap(mask, mRect, mRect, null);

            canvas.restoreToCount(save);

        }
    }


    static class TranslationSwitcher extends SwitchDrawer {

        // 0 up :down -> top 进入动画
        // 1 down : top -> down 退出动画
        private int arrow = 0;

        public TranslationSwitcher(PhotoView container, View mater, View slave) {
            super(container, mater, slave);
        }

        @Override
        public void disptachDraw(Canvas canvas) {
            int save = canvas.save();
            int height = continer.getHeight();

            if (arrow == 0) {
                //down -> top
                master.draw(canvas);
                canvas.translate(master.getScrollX(), master.getScrollY() + height * (1f - percent));
                slave.draw(canvas);
            } else {
                slave.draw(canvas);
                canvas.translate(master.getScrollX(), master.getScrollY() + height * percent);
                master.draw(canvas);
            }
            canvas.translate(0f, 0f);
            canvas.restoreToCount(save);

        }
    }

    static class RectSwitcher extends SwitchDrawer {

        private final Rect rect = new Rect();

        public RectSwitcher(PhotoView container, View mater, View slave) {
            super(container, mater, slave);
        }

        Path p = new Path();

        @Override
        public void disptachDraw(Canvas canvas) {
            int save = canvas.save();
            slave.draw(canvas);
            int width = continer.getWidth();
            int height = continer.getHeight();
            Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            Canvas maskCanvas = new Canvas(mask);
            maskCanvas.translate(master.getScrollX(), master.getScrollY());
            //生成bitmap
            master.draw(maskCanvas);
            int hw = continer.getWidth() / 2;
            int hp = continer.getHeight() / 2;
            int start = (int) (hw * percent + 0.5f);
            int end = continer.getWidth() - start;
            int top = (int) (hp * percent + 0.5f);
            int bottom = continer.getHeight() - top;
            rect.set(start, top, end, bottom);
            canvas.drawBitmap(mask, rect, rect, null);
            canvas.restoreToCount(save);

        }

    }

    static class CircleSwitcher extends SwitchDrawer {


        public CircleSwitcher(PhotoView container, View mater, View slave) {
            super(container, mater, slave);
        }

        private Path p = new Path();

        @Override
        public void disptachDraw(Canvas canvas) {
            int save = canvas.save();
            slave.draw(canvas);
            int width = continer.getWidth();
            int height = continer.getHeight();
            Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            Canvas maskCanvas = new Canvas(mask);
            maskCanvas.translate(master.getScrollX(), master.getScrollY());
            //生成bitmap
            master.draw(maskCanvas);

            float max = Math.max(width, height);
            float radius = (float) (Math.sqrt(max * max) / 2f) * (1f - percent);
            p.reset();
            p.addCircle(width / 2f, height / 2f, radius, Path.Direction.CW);
            canvas.clipPath(p);
            canvas.drawBitmap(mask, 0, 0, null);

//            int hw = continer.getWidth() / 2;
//            int hp = continer.getHeight() / 2;
//            int start = (int) (hw * percent + 0.5f);
//            int end = continer.getWidth() - start;
//            int top = (int) (hp * percent + 0.5f);
//            int bottom = continer.getHeight() - top;
//            rect.set(start, top, end, bottom);
//            canvas.drawBitmap(mask, rect, rect, null);
            canvas.restoreToCount(save);

        }

    }

    /**
     * 百叶窗进入
     */
    static class ShutterInSwitcher extends SwitchDrawer {

        private final Rect drawRect = new Rect();
        private int mLeafCount = 5;
        private int mOriention = LinearLayout.HORIZONTAL;
        private final Paint mPaint = new Paint();


        public ShutterInSwitcher(PhotoView container, View mater, View slave) {
            super(container, mater, slave);
        }

        public void setLeafCount(int leafCount) {
            this.mLeafCount = mLeafCount;
        }

        public void setOriention(int oriention) {
            this.mOriention = oriention;
        }

        @Override
        public void disptachDraw(Canvas canvas) {
            int width = continer.getWidth();
            int height = continer.getHeight();
            // 清空画布
            canvas.drawColor(Color.TRANSPARENT);

            // 设置离屏缓存
            int save = canvas.save();

            // 绘制底部图像
            master.draw(canvas);
            // 设置混合模式（只在源图像和目标图像相交的地方绘制目标图像）
//            mPaint.setXfermode(PorterDuffXfermode(mMode))

            Bitmap mask = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            Canvas maskCanvas = new Canvas(mask);
            slave.draw(maskCanvas);
            // 再绘制源图像的遮罩
            for (int i = 0; i < mLeafCount; i++) {
                if (mOriention == LinearLayout.HORIZONTAL) { // 水平方向
                    int column_width = (int) Math.ceil(width * 1f / mLeafCount);
                    int left = column_width * i;
                    int right = (int) (left + column_width * percent);
                    drawRect.set(left, 0, right, height);
                    // 在遮罩画布上绘制各矩形叶片
                    canvas.drawBitmap(
                            mask,
                            drawRect,
                            drawRect,
                            mPaint
                    );
                } else { // 垂直方向
                    int row_height = (int) Math.ceil(height * 1f / mLeafCount);
                    int top = row_height * i;
                    int bottom = (int) (top + row_height * percent);
                    drawRect.set(0, top, width, bottom);
                    canvas.drawBitmap(
                            mask,
                            drawRect,
                            drawRect,
                            mPaint
                    );
                    // 在遮罩画布上绘制各矩形叶片
                }
            }
            // 还原画布
            canvas.restoreToCount(save);
        }

    }

    static class ComposeRandomSwitcher extends SwitchDrawer {


        private boolean random = true;
        private final List<SwitchDrawer> childDrawers = new ArrayList<>();
        private SwitchDrawer currentDrawer;


        public ComposeRandomSwitcher(PhotoView container, View mater, View slave) {
            super(container, mater, slave);
        }


        public void setChildDrawers(List<SwitchDrawer> childDrawers) {
            this.childDrawers.clear();
            this.childDrawers.addAll(childDrawers);
            if (childDrawers.isEmpty()) {
                childDrawers.add(new CurtainSwitcher(continer, master, slave));
            }
            if (random) {
                currentDrawer = this.childDrawers.get((int) (Math.random() * childDrawers.size()));
            }
        }

        @Override
        public void decoderAnimator(Animator animator) {
            next();
            super.decoderAnimator(animator);
        }

        public void next() {
            if (random) {
                currentDrawer = this.childDrawers.get((int) (Math.random() * childDrawers.size()));
            } else {
                try {
                    currentDrawer = this.childDrawers.get(Math.max(0, this.childDrawers.indexOf(currentDrawer) + 1));
                } catch (Exception e) {
                    currentDrawer = this.childDrawers.get(0);
                }
            }
        }


        @Override
        public void disptachDraw(Canvas canvas) {
            if (currentDrawer != null) {
                currentDrawer.percent = percent;
                currentDrawer.disptachDraw(canvas);
            }
        }


        @Override
        public void cancel() {
            super.cancel();
        }
    }

    static class LazyUpdateDrawer {

        private SwitchDrawer currentDrawer;
        private SwitchDrawer nextDrawer = null;

        public LazyUpdateDrawer(SwitchDrawer none) {
            this.currentDrawer = none;
        }

        public void appendNextDrawer(SwitchDrawer switchDrawer) {
            this.nextDrawer = switchDrawer;
        }

        public void markAppendCommit() {
            if (nextDrawer != null) {
                Log.d(TAG, "markAppendCommit:1 " + currentDrawer);
                Log.d(TAG, "markAppendCommit:2 " + nextDrawer);
                this.currentDrawer = nextDrawer;
                this.nextDrawer = null;
            }
        }

        @NonNull
        public SwitchDrawer getCurrentDrawer() {
            return currentDrawer;
        }

        public void cancel() {
            currentDrawer.cancel();
            this.nextDrawer = null;
        }
    }
}
